package com.tim.apps.mockgps.model;

import android.location.Location;

import com.google.android.maps.GeoPoint;

public class GPSPosition {

	private double lat;
	private double lng;
	private double altitude;
	private double bearing;
	private double speedMPerSec;
	private double accuracy;
	
	public String toString()
	{
		StringBuilder buf = new StringBuilder();
		buf.append("lat=" + lat);
		buf.append(" lng=" + lng);
		buf.append(" bearing=" + bearing);
		
		return buf.toString();
	}

	public GPSPosition(
			double lat, double lng, double altitude, double bearing,
			double speed, double accuracy) {
		this.lat = lat;
		this.lng = lng;
		this.altitude = altitude;
		this.bearing = bearing;
		this.speedMPerSec = speed;
		this.accuracy = accuracy;
	}
	
	public double getSpeedMPerSec() {
		return speedMPerSec;
	}

	public void setSpeedMPerSec(double speed) {
		this.speedMPerSec = speed;
	}

	/**
	 * @return the lat
	 */
	public final double getLat() {
		return lat;
	}

	/**
	 * @param lat the lat to set
	 */
	public final GPSPosition setLat(double lat) {
		this.lat = lat;
		return this;
	}

	/**
	 * @return the lng
	 */
	public final double getLng() {
		return lng;
	}

	/**
	 * @param lng the lng to set
	 */
	public final GPSPosition setLng(double lng) {
		this.lng = lng;
		return this;
	}

	/**
	 * @return the altitude
	 */
	public final double getAltitude() {
		return altitude;
	}

	/**
	 * @param altitude the altitude to set
	 */
	public final GPSPosition setAltitude(double altitude) {
		this.altitude = altitude;
		return this;
	}

	/**
	 * @return the bearing
	 */
	public final double getBearing() {
		return bearing;
	}
	
	public final GPSPosition setBearing(double bearing) {
		this.bearing = bearing;
		return this;
	}

	/**
	 * @return the accuracy
	 */
	public final double getAccuracy() {
		return accuracy;
	}

	/**
	 * @param accuracy the accuracy to set
	 */
	public final GPSPosition setAccuracy(double accuracy) {
		this.accuracy = accuracy;
		return this;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	@Override
	public GPSPosition clone() {
		return new GPSPosition(lat, lng, altitude, bearing, speedMPerSec, accuracy);
	}

	public void writeTo(Location location) {
		location.setLatitude(lat);
		location.setLongitude(lng);
		location.setAltitude(altitude);
		location.setBearing((float) bearing);
		location.setSpeed((float) speedMPerSec);
		location.setAccuracy((float) accuracy);
	}
	
	public static double bearing(GPSPosition from, GPSPosition to) {
		
		if (to.lat == from.lat && to.lng == from.lng) {
			return to.bearing;
		}

		double deltaLat = to.lat - from.lat;
		double deltaLng = to.lng - from.lng;
		
		double bearing = Math.atan2(deltaLng, deltaLat) * (180.0 / Math.PI);
		if (bearing < 0) {
			bearing += 360.0;
		}
		return bearing;
	}
	
	public static GPSPosition interpolate(GPSPosition from, GPSPosition to,
			double factor)
	{
		double bearingDelta = to.bearing - from.bearing;
		
		if (bearingDelta > 180.0) {
			bearingDelta -= 360.0;
		}
		else if (bearingDelta < -180.0) {
			bearingDelta += 360.0;
		}
		
		return new GPSPosition(
				from.lat * (1.0-factor) + to.lat * factor,
				from.lng * (1.0-factor) + to.lng * factor,
				from.altitude * (1.0-factor) + to.altitude * factor,
				normalizeAngle(from.bearing + bearingDelta * factor),
				from.speedMPerSec * (1.0-factor) + to.speedMPerSec * factor,
				from.accuracy * (1.0-factor) + to.accuracy * factor
				);
	}
	
	public double distanceKmTo(GPSPosition other) {
		return GeoCalcUtils.greatCircleDistanceKm(
				this.lat, this.lng,
				other.lat, other.lng);
	}
	
	private static double normalizeAngle(double angle) {
		while (angle < 0.0) {
			angle += 360.0;
		}
		while (angle >= 360.0) {
			angle -= 360.0;
		}
		return angle;
	}
}
